#include "object3d.h"
#include "drawing_utility.h"

#include <QtOpenGL>
#include "glut.h"

// MATRIX4x4
Matrix4x4::Matrix4x4()
{
  data = new float*[4];
  data[0] = new float[4];
  data[0][0] = 1.0f; data[0][1] = 0.0f; data[0][2] = 0.0f; data[0][3] = 0.0f;
  data[1] = new float[4];
  data[1][0] = 0.0f; data[1][1] = 1.0f; data[1][2] = 0.0f; data[1][3] = 0.0f;
  data[2] = new float[4];
  data[2][0] = 0.0f; data[2][1] = 0.0f; data[2][2] = 1.0f; data[2][3] = 0.0f;
  data[3] = new float[4];
  data[3][0] = 0.0f; data[3][1] = 0.0f; data[3][2] = 0.0f; data[3][3] = 1.0f;
}

Matrix4x4::Matrix4x4(const Matrix4x4& m)
{
  data = new float*[4];
  for (int i = 0; i < 4; ++i)
  {
    data[i] = new float[4];
    for (int j = 0; j < 4; ++j)
      data[i][j] = m.data[i][j];
  }
}
Matrix4x4::~Matrix4x4()
{
  delete[] data[0];
  delete[] data[1];
  delete[] data[2];
  delete[] data[3];
  delete[] data;
}

float* Matrix4x4::operator [] (int i)
{
  return data[i];
}

Matrix4x4& Matrix4x4::operator = (const Matrix4x4& m)
{
  for (int i = 0; i < 4; ++i)
    for (int j = 0; j < 4; ++j)
      data[i][j] = m.data[i][j];
  return (*this);
}
Matrix4x4 Matrix4x4::translateMatrix(float x, float y, float z)
{
  Matrix4x4 res;
  res.data[0][3] = x; res.data[1][3] = y; res.data[2][3] = z;
  return res;
}
Matrix4x4 Matrix4x4::scaleMatrix(float sx, float sy, float sz)
{
  Matrix4x4 res;
  res.data[0][0] = sx; res.data[1][1] = sy; res.data[2][2] = sz;
  return res;
}
Matrix4x4 Matrix4x4::rotateMatrix(float alpha, RotationType type)
{
  Matrix4x4 res;
  float cosAlpha = cos(alpha);
  float sinAlpha = sin(alpha);
  switch (type)
  {
  case ROT_XY:
    res.data[0][0] = cosAlpha;  res.data[0][1] = sinAlpha;
    res.data[1][0] = -sinAlpha; res.data[1][1] = cosAlpha;
    break;
  case ROT_XZ:
    res.data[0][0] = cosAlpha;  res.data[0][2] = sinAlpha;
    res.data[2][0] = -sinAlpha; res.data[2][2] = cosAlpha;
    break;
  case ROT_YZ:
    res.data[1][1] = cosAlpha;  res.data[1][2] = sinAlpha;
    res.data[2][1] = -sinAlpha; res.data[2][2] = cosAlpha;
    break;
  }
  return res;
}


Matrix4x4 operator * (const Matrix4x4 m1, const Matrix4x4& m2)
{
  Matrix4x4 res;
  float sum;
  int i, j, k;
  for (i = 0; i < 4; ++i)
    for (j = 0; j < 4; ++j)
    {
      sum = 0.0f;
      for (k = 0; k < 4; ++k)
        sum += m1.data[i][k] * m2.data[k][j];
      res.data[i][j] = sum;
    }
  return res;
}


void setGlModelViewMatrix(const Matrix4x4& m)
{
  GLfloat mt[16];
  for (int i = 0; i < 4; ++i)
    for (int j = 0; j < 4; ++j)
      mt[i * 4 + j] = m.data[j][i];
  glMatrixMode(GL_MODELVIEW);
  //glLoadIdentity();
  glLoadMatrixf(mt);
  //glMultMatrixf((const GLfloat*)mt);
}
// ABSTRACT OBJECT 3D
void AbstractObject3D::translate(float x, float y, float z)
{
  worldMatrix = Matrix4x4::translateMatrix(x, y, z) * worldMatrix;
}
void AbstractObject3D::rotate(float alpha, RotationType type)
{
  worldMatrix = Matrix4x4::rotateMatrix(alpha, type) * worldMatrix;
}
void AbstractObject3D::zoom(float z)
{
  worldMatrix = Matrix4x4::scaleMatrix(z, z, z) * worldMatrix;
}
void AbstractObject3D::transform(const Matrix4x4& m)
{
  worldMatrix = m * worldMatrix;
}
// TRAJECTION OBJECT 3D
TrajectionObject3D::TrajectionObject3D(const QVector<Point3D>& pointList, Point3D center, float scale, int color)
{
  cr = ((color & 0xff0000) >> 16) / 255.0f;
  cg = ((color & 0x00ff00) >> 8) / 255.0f;
  cb = (color & 0x0000ff) / 255.0f;
  for (QVector<Point3D>::const_iterator it = pointList.begin(); it != pointList.end(); it++)
    m_pointList.push_back(scale * ((*it) - center));
}
void TrajectionObject3D::render() const
{
  if (!m_pointList.empty())
  {
    setGlModelViewMatrix(worldMatrix);
    glBegin(GL_LINES);
    glColor3f(cr, cg, cb);
    for (unsigned i = 1; i < m_pointList.size(); ++i)
    {
      glVertex3d(m_pointList[i - 1].x, m_pointList[i - 1].y, m_pointList[i - 1].z);
      glVertex3d(m_pointList[i].x, m_pointList[i].y, m_pointList[i].z);
    }
    glEnd();
    glFlush();
  }
}
// TEXT OBJECT 3D
TextObject3D::TextObject3D(QString text) : m_text(text)
{
}
void TextObject3D::render() const
{
  setGlModelViewMatrix(worldMatrix);
  glColor3f(1.0, 1.0, 0.0);
  for (unsigned i = 0; i < m_text.size(); ++i)
    glutStrokeCharacter(GLUT_STROKE_ROMAN, (char)(m_text.toAscii()[i]));
}
// PLANET OBJECT 3D
const int PlanetObject3D::DRAW_PLANET = 0x00f;
const int PlanetObject3D::DRAW_TRAJECTORY = 0x0f0;
const int PlanetObject3D::DRAW_CAPTION = 0xf00;
int PlanetObject3D::drawingType = DRAW_PLANET | DRAW_TRAJECTORY | DRAW_CAPTION;

PlanetObject3D::PlanetObject3D(float r, QString name, const QVector<Point3D>& pointList, Point3D center, float scale, unsigned color)
  : trajectory(pointList, center, scale, color)
  , caption(name)
{
  zoom(r);
  translate(scale * (pointList[0].x - center.x), 
            scale * (pointList[0].y - center.y), 
            scale * (pointList[0].z - center.z));
  caption.rotate(-pi / 2.0, ROT_YZ);
  caption.zoom(0.001);
  caption.translate(scale * (pointList[0].x - center.x) + r, 
                    scale * (pointList[0].y - center.y), 
                    scale * (pointList[0].z - center.z));
  cr = ((color & 0xff0000) >> 16) / 255.0f;
  cg = ((color & 0x00ff00) >> 8) / 255.0f;
  cb = (color & 0x0000ff) / 255.0f;
}
void PlanetObject3D::render() const
{
  //glDevice->qglColor(Qt::blue);
  glColor3f(cr, cg, cb);
  if (drawingType & DRAW_PLANET)
  {
    setGlModelViewMatrix(worldMatrix);
    drawSolidSphere(1.0, 1000, 1000);
  }
  if (drawingType & DRAW_CAPTION)
    caption.render();
  if (drawingType & DRAW_TRAJECTORY)
    trajectory.render();
  //qglColor(Qt::black);
}

int PlanetObject3D::getDrawingType()
{
  return drawingType;
}
void PlanetObject3D::setDrawingType(int dt)
{
  drawingType = dt;
}

